package uk.ac.ebi.bioinvindex.dao.ejb3;

import org.apache.commons.beanutils.ConstructorUtils;
import uk.ac.ebi.bioinvindex.dao.BIIDAOException;
import uk.ac.ebi.bioinvindex.model.impl.term.MaterialRoleImpl;
import uk.ac.ebi.bioinvindex.model.impl.term.OntologyEntryImpl;
import uk.ac.ebi.bioinvindex.model.impl.xref.ReferenceSourceImpl;
import uk.ac.ebi.bioinvindex.model.impl.xref.ReferenceSources;

import java.util.List;

/**
 * User: Nataliya Sklyar (nsklyar@ebi.ac.uk)
 * Date: Sep 20, 2007
 */
public class OntologyEntryDAOHelper extends AbstractEJB3DAO {

//	public static final String REFERENCE_SOURCE_OBI = "OBI";

//	public MaterialRole getMaterialTypeSource() {
//		ReferenceSource source = getReferenceSourceByName(ReferenceSources.OBI.getName());
//
//		List results = entityManager.createQuery(
//				"select m from MaterialRoleImpl m where name=\'source\' " +
//						"and source = :source").setParameter("source", source).getResultList();
//
//		if (results.size() != 1) {
//			throw new BIIDAOException("MaterialRole \"Source\" does not exist or more then one entries " +
//					"correspond to a name");
//		}
//
//		return (MaterialRole) results.get(0);
//	}

	public MaterialRoleImpl getMaterialTypeByAcc(String typeAcc) {
		ReferenceSourceImpl source = getReferenceSourceByName(ReferenceSources.OBI.getName());

		List results = entityManager.createQuery(
				"select m from MaterialRoleImpl m where acc =:acc  " +
						"and source = :source")
				.setParameter("source", source)
				.setParameter("acc", typeAcc)
				.getResultList();

		if (results.size() != 1) {
			throw new BIIDAOException("MaterialRole \"Source\" does not exist or more then one entries " +
					"correspond to a name");
		}

		return (MaterialRoleImpl) results.get(0);
	}

	@Deprecated
	/**
	 * moved to ReferenceSourceEJB3DAO
	 */
	public ReferenceSourceImpl getReferenceSourceByName(String name) {
		List results = entityManager.createQuery(
				"select r from ReferenceSourceImpl r where name = :name").
				setParameter("name", name).getResultList();

		if (results.size() != 1) {
			throw new BIIDAOException("Reference source does not exist or more then one source " +
					"correspond to a name : " + name);
		}

		return (ReferenceSourceImpl) results.get(0);
	}


	@Deprecated
	/**
	 * moved to ReferenceSourceEJB3DAO
	 */
	public ReferenceSourceImpl getReferenceSourceByAcc(String acc) {
		List results = entityManager.createQuery(
				"select r from ReferenceSourceImpl r where acc = :acc").
				setParameter("acc", acc).getResultList();

		if (results.size() != 1) {
			throw new BIIDAOException("Reference source does not exist or more then one source " +
					"correspond to a name : " + acc);
		}

		return (ReferenceSourceImpl) results.get(0);
	}


	@Deprecated
	/**
	 * moved to ReferenceSourceEJB3DAO
	 *
	 * if checkExists is true and more than 1 results is returned (or 0) raises an exception.
	 * Otherwise returns null.
	 *
	 * @param acc
	 * @param checkExists
	 * @return
	 */
	public ReferenceSourceImpl getReferenceSourceByAcc(String acc, boolean checkExists) {
		List results = entityManager.createQuery(
				"select r from ReferenceSourceImpl r where acc = :acc").
				setParameter("acc", acc).getResultList();

		int sz = results.size();

		if (checkExists) {
			if (sz != 1)
				throw new BIIDAOException("Reference source does not exist or more than one source " +
						"correspond to a name : " + acc);
			else
				return (ReferenceSourceImpl) results.get(0);
		} else {
			if (sz == 0)
				return null;
			else
				return (ReferenceSourceImpl) results.get(0);
		}
	}


	/**
	 * Defaults to checkExists = true
	 */
	public OntologyEntryImpl getOntologyEntryByAcc(String acc) {
		return getOntologyEntryByAcc(acc, true);
	}


	public OntologyEntryImpl getOntologyEntryByAcc(String acc, boolean checkExists) {
		List results = entityManager.createQuery(
				"select oe from OntologyEntryImpl oe where acc = :acc").
				setParameter("acc", acc).getResultList();

		int sz = results.size();

		if (checkExists) {
			if (sz != 1)
				throw new BIIDAOException("Reference source does not exist or more than one source " +
						"correspond to a name : " + acc);
			else
				return (OntologyEntryImpl) results.get(0);
		} else {
			if (sz == 0)
				return null;
			else
				return (OntologyEntryImpl) results.get(0);
		}
	}


	@Deprecated
	/**
	 * Creates an ontology entry on the basis of its attributes
	 *
	 * @param acc			 the OE accession. Returns an existing OE in case it exists. Returns an error in case
	 *                  the accession exists and has a different ontology entry wrt sourceAcc.
	 * @param sourceAcc the Ontology Source accession, must exist.
	 * @param oeClass	 the specific OntologyEntry type you want to create.
	 * @return a new OE. The new instance is not persisted on the DB, this is up to the caller.
	 */
	public OntologyEntryImpl getOntologyTermFromFields(String acc, String sourceAcc, Class oeClass) {
		if (acc == null || acc.length() == 0)
			throw new BIIDAOException("Cannot create an ontology term with null accession");

		OntologyEntryImpl oe = null;
		oe = getOntologyEntryByAcc(acc, false);

		if (oe == null) {

			// Doesn't exist, create it
			//

			try {
				oe = (OntologyEntryImpl) ConstructorUtils.invokeConstructor(
						oeClass, new String[]{sourceAcc, ""}
				);
			}
			catch (Exception e) {
				// TODO: proper exception
				throw new RuntimeException("Cannot create a new OntologyEntry", e);
			}

			oe.setAcc(acc);

			ReferenceSourceImpl source = getReferenceSourceByAcc(sourceAcc, false);

			if (source == null)
				throw new BIIDAOException("Cannot find the ontology source for the accession '" + sourceAcc + "'");

			oe.setSource(source);

			return oe;
		} else {
			// check that the existing entry has correct source
			//

			boolean isok = true;

			ReferenceSourceImpl osrc = oe.getSource();
			if (osrc == null) {
				isok = sourceAcc == null || sourceAcc.length() == 0;
			} else {
				isok = sourceAcc == null || sourceAcc.equals(osrc.getAcc());
			}

			if (!isok) {
				throw new BIIDAOException(String.format(
						"Mismatch of Ontology Entry '%s' with Ontology Source '%s' ", acc, sourceAcc)
				);
			}

			return oe;
		}
	}

@Deprecated
	public ReferenceSourceImpl getReferenceSourceFromFields(String acc, String url, String version, String description) {
		if (acc == null || acc.length() == 0)
			throw new BIIDAOException("Cannot create an ontology term with null accession");

		ReferenceSourceImpl src = this.getReferenceSourceByAcc(acc, false);
		if (src == null) {
			src = new ReferenceSourceImpl("");
			src.setAcc(acc);
			src.setUrl(url);
			//TODO: version is ignored at the moment
			//TODO: description is ignored at the moment
		}
		return src;
	}


}
